package my.test.csdn.weixin_43767015.basic.ch12.transaction.declarative.annotation.propagation.requires_new.eachcall.service;

import javax.annotation.Resource;

import org.springframework.aop.framework.AopContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import my.test.bean.ex14.synchronization.TransactionManageService;
import my.test.csdn.weixin_43767015.basic.ch12.transaction.declarative.annotation.synchronization.TransactionalMessageSender;

@Component
public class TxRequiresNewEachService {
    /**
     * jdbc模版，用于操作数据库
     */
    @Resource
    private JdbcTemplate jdbcTemplate;
    
    /** 在开启了事务的方法m1中调用m2 */
	@Transactional(propagation = Propagation.REQUIRED)
    public void m1() {
		//m1 do something1
        String sql = "insert into tx_study (name,age) values (?,?)";
        jdbcTemplate.update(sql, "TxRequired_m1_something1", 30);
        /** 外层事务注册同步 */
		TransactionSynchronizationManager.registerSynchronization(new TransactionalMessageSender());
        TxRequiresNewEachService o = (TxRequiresNewEachService)AopContext.currentProxy();
        o.m2();
        //m1 do something2
        jdbcTemplate.update(sql, "TxRequired_m1_something2", 30);
        //抛出一个RuntimeException
        throw new RuntimeException();
	}
	
	@Transactional(propagation = Propagation.REQUIRES_NEW)
	public void m2() {
        String sql = "insert into tx_study (name,age) values (?,?)";
        jdbcTemplate.update(sql, "TxRequiresNew_m2", 30);
        /** 内层事务注册同步 */
		//注册事务提交后的操作  
		TransactionManageService.afterCommit(() -> System.out.println("Transaction committed successfully!"));
		//注册事务回滚后的操作
		TransactionManageService.afterRollback(() -> System.out.println("Transaction rolledback!"));	
        //抛出一个RuntimeException
        //throw new RuntimeException();		
	}

	/**
	       将会创建两个物理级别的事务，它们之间没有联系，并且外部事物会先被挂起，内部事务执行完毕之后再恢复
	 */
    @Resource
    private DataSourceTransactionManager transactionManager;
	
    public DataSourceTransactionManager getTransactionManager() {
    	return transactionManager;
    }
    
    /**
     * 只是用来看
     */
    @Deprecated
    public void tran() {
        //获得一个事务管理器
        DataSourceTransactionManager txManager = getTransactionManager();
        //创建一个事务定义对象，可以设置事务的属性
        DefaultTransactionDefinition m1def = new DefaultTransactionDefinition();
        //只能以编程方式执行时才能显式设置事务名称
        m1def.setName("ts1Name");
        m1def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //m1开启一个新事务ts1
        TransactionStatus ts1 = txManager.getTransaction(m1def);
        
        try {
			//m1 do something1
			String sql = "insert into tx_study (name,age) values (?,?)";
			jdbcTemplate.update(sql, "TxRequired_m1_something1", 30);
			//挂起m1的ts1事务
			//这个方法实际上由Spring来调用，位于AbstractPlatformTransactionManager中
			/** https://blog.csdn.net/Onion_521257/article/details/122593308
			 *  protected关键字
			 *  对于类的调用者来说,protected修饰的字段和方法是不能访问的; 
			 *  对于类的子类和同一个包的其他类来说,protected修饰的字段和方法是可以访问的.
			 */
			suspend(((DefaultTransactionStatus)ts1).getTransaction());
			
			//创建一个事务定义对象，可以设置事务的属性
			DefaultTransactionDefinition m2def = new DefaultTransactionDefinition();
			//只能以编程方式执行时才能显式设置事务名称
			m2def.setName("ts2Name");
			m2def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
			//m2开启一个新事务ts2
			TransactionStatus ts2 = txManager.getTransaction(m2def);
			try {
			    //执行m2方法业务逻辑
			    m2();
			} catch (Throwable ex) {
			    //回滚ts2事务
			    txManager.rollback(ts2);
			}
			//提交ts2事务
			txManager.commit(ts2);
			
			//恢复m1的ts1事务
			//这个方法实际上由Spring来调用，位于AbstractPlatformTransactionManager中
			resume(((DefaultTransactionStatus)ts2).getTransaction(),(SuspendedResourcesHolder)((DefaultTransactionStatus)ts2).getSuspendedResources());
   
			//m1 do something2
			jdbcTemplate.update(sql, "TxRequired_m1_something2", 30);
		} catch (Throwable throwable) {
			//回滚ts1
	        txManager.rollback(ts1);
		}       
        //提交ts1
        txManager.commit(ts1);
    }
    
	/** 
     * 挂起给定的事务
     * 模拟org.springframework.transaction.support.AbstractPlatformTransactionManager.suspend(Object)
     */
    private SuspendedResourcesHolder suspend(@Nullable Object transaction) {
    	return null;
    }
    
    /**
     * 恢复外层挂起事务
     * 模拟org.springframework.transaction.support.AbstractPlatformTransactionManager.resume(Object, SuspendedResourcesHolder)
     */
	protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder) {
		
	}
    
    protected static final class SuspendedResourcesHolder {
    	
    }
}
